package com.hu.common.util.adm;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 初始化单例类、实体类、接口需要的bean，因为单例类无法直接注入bean
 */
@Component
@Slf4j
@RequiredArgsConstructor
@Order(-1) // 这里优先级一定要最高，否则其他的初始化使用到路径会报错
public class SpringKit implements ApplicationContextAware {

    /**
     * 缓存从 spring application context 获取到的 bean， 避免每次通过反射获取
     */
    private static final Map<String, Object> BEAN_MAP = new ConcurrentHashMap<>();
    private static ApplicationContext APP_CONTEXT;

    /**
     * 获取 Spring Context 对象
     * @return ApplicationContext
     */
    public static ApplicationContext getAppContext() {
        return APP_CONTEXT;
    }

    /**
     * 获取 bean， 并缓存到 map 集合，  避免每次通过反射获取
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(final Class<T> clazz) {
        if (!BEAN_MAP.containsKey(clazz.getName())) {
            // 缓存从 spring application context 获取到的 bean， 避免每次通过反射获取
            BEAN_MAP.put(clazz.getName(), APP_CONTEXT.getBean(clazz));
        }
        return (T) BEAN_MAP.get(clazz.getName());
    }

    /**
     * 获取 bean， 并缓存到 map 集合，  避免每次通过反射获取
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(final String beanName, final Class<T> clazz) {
        if (!BEAN_MAP.containsKey(beanName)) {
            // 缓存从 spring application context 获取到的 bean， 避免每次通过反射获取
            BEAN_MAP.put(beanName, APP_CONTEXT.getBean(beanName, clazz));
        }
        return (T) BEAN_MAP.get(clazz.getName());
    }

    public static <T> T getBean(final String beanName) {
        if (!BEAN_MAP.containsKey(beanName)) {
            // 缓存从 spring application context 获取到的 bean， 避免每次通过反射获取
            BEAN_MAP.put(beanName, APP_CONTEXT.getBean(beanName));
        }
        return (T) BEAN_MAP.get(beanName);
    }

    public static Map<String, Object> getBeansWithAnnotation(final Class<? extends Annotation> annotation) {
        return APP_CONTEXT.getBeansWithAnnotation(annotation);
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        APP_CONTEXT = applicationContext;
    }

}
